package com.buddy.sds.auth.realm;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.buddy.sds.auth.entity.*;
import com.buddy.sds.auth.oauth2.entity.SystemOauthToken;
import com.buddy.sds.auth.oauth2.token.TokenManager;
import com.buddy.sds.auth.service.ISystemUserService;
import com.buddy.sds.auth.utils.JWTUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.List;

/**
 * OAuth认证、授权域
 */
@Slf4j
public class OAuthRealm extends AuthorizingRealm {

    @Autowired
    private TokenManager tokenManager;

    @Autowired
    private ISystemUserService userService;


    public OAuthRealm(){
        //设置凭据校验器
        this.setCredentialsMatcher(new OauthCredentialsMatcher());
    }



    class OauthCredentialsMatcher implements CredentialsMatcher {

        @Override
        public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {

            String token = (String) authenticationToken.getCredentials();

            return tokenManager.verifyToken(token);
        }
    }


    /**
     * 获取权限认证信息
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();


        SystemUser systemUser = (SystemUser) principalCollection.getPrimaryPrincipal();


        if(systemUser != null) {

            List<SystemRole> systemRoles = systemUser.getRoles();

            if (systemRoles != null && !systemRoles.isEmpty()) {

                systemRoles.forEach(r -> {

                    //加入用户所拥有的角色
                    authorizationInfo.addRole(r.getRoleName());

                    List<SystemPermission> permissions = r.getPermissions();

                    if (permissions != null && !permissions.isEmpty()) {

                        permissions.forEach(p -> {
                            //加入url
                            String url = p.getPermissionUrl();
                            if(!StringUtils.isEmpty(url)){

                                authorizationInfo.addStringPermission(url);
                            }

                        });
                    }

                });
            }
        }


        return authorizationInfo;
    }


    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof OAuthToken;
    }

    /**
     * 获取身份认证信息
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        String token = (String) authenticationToken.getCredentials();

        String username = tokenManager.getUser(token);
        if(username == null){
            return null;
        }

        //根据用户名获取用户信息
        SystemUser user = userService.loadByUsername(username,true);

        if(user == null){
            return null;
        }

        if(user.isUserLock()){

            //账户已被冻结


            log.error("账号:{}已被锁定",user);

            throw new LockedAccountException();
        }

        LocalDateTime expiretime = user.getUserExpire();

        if(expiretime != null){

            if(expiretime.isBefore(LocalDateTime.now())){
                //账户已过期

                log.error("账号:{}已过期",user);
                throw new ExpiredCredentialsException();
            }
        }

        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(

                user,user.getUserPassword(),
                this.getName()

        );

        return authenticationInfo;
    }



}
